/**
 * FileName: NettyCallbackCenter
 * Author: Mr.Wang
 * Date: 2025/10/16 10:44
 * Description:
 * History:
 */
package org.test.device.config.netty;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.test.device.utils.netty.NettyCallbackUtil;
import org.test.device.utils.netty.NettyClientManager;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 〈一句话功能简述〉<br>
 * 〈异步netty回调中心〉
 *
 * @author Mr.Wang
 * @create 2025/10/16
 * @since 1.0.0
 */
@Slf4j
@Component
public class NettyCallbackCenter {

    @Autowired
    NettyClientManager nettyClientManager;

    @Data
    private static class CallbackWrapper {
        private NettyCallback callback; // 业务Service的回调
        private String businessTag;     // 业务标签
        private String originalMsg;     // 原始发送消息
        private String finalMsg; // 存储含messageId的最终发送消息
        private int retryCount = 0;     // 重试次数
    }

    // 单例实例（线程安全）
    private static final NettyCallbackCenter INSTANCE = new NettyCallbackCenter();
    // 全局回调映射：key=消息ID，value=回调+业务信息封装
    private final Map<String, CallbackWrapper> callbackMap = new ConcurrentHashMap<>();
    // 最大超时时间（10秒，可配置）
    private static final long TIMEOUT_SECONDS = 10;

    // 私有构造，防止外部实例化
    private NettyCallbackCenter() {}

    public String registerCallback(NettyCallback callback, String businessTag, String originalMsg, String finalMsg) {
        // 生成唯一消息ID（确保全局不重复）
        String messageId = "NETTY_MSG_" + UUID.randomUUID().toString().replace("-", "");
        // 封装回调+业务信息
        CallbackWrapper wrapper = new CallbackWrapper();
        wrapper.setCallback(callback);
        wrapper.setBusinessTag(businessTag);
        wrapper.setOriginalMsg(originalMsg);
        wrapper.setFinalMsg(finalMsg);
        // 注册到全局Map
        callbackMap.put(messageId, wrapper);
        // 注册超时任务：超过时间未收到响应，触发失败回调
        scheduleTimeoutCallback(messageId);
        return messageId;
    }

    public void triggerSuccess(String messageId, String serverResponse) {
        // 从Map中移除（避免重复触发）
        CallbackWrapper wrapper = callbackMap.remove(messageId);
        if (wrapper == null) {
            log.warn("[NettyCallbackCenter]消息ID[{}]无对应回调，可能已超时或触发", messageId);
            return;
        }
        try {
            // 1. 调用通用成功处理（日志、通用业务）
            NettyCallbackUtil.commonOnSuccess(
                    wrapper.getBusinessTag(),
                    wrapper.getOriginalMsg(),
                    serverResponse
            );
            // 2. 触发业务Service的自定义成功回调
            wrapper.getCallback().onSuccess(serverResponse);
        } catch (Exception e) {
            log.error("[NettyCallbackCenter]触发成功回调异常，消息ID[{}]", messageId, e);
        }
    }

    public void triggerFailure(String messageId, Throwable error) {
        CallbackWrapper wrapper = callbackMap.remove(messageId);
        if (wrapper == null) {
            log.warn("[NettyCallbackCenter]消息ID[{}]无对应回调，可能已超时或触发", messageId);
            return;
        }
        try {
            // 1. 调用通用失败处理（日志、重试、告警）
            NettyCallbackUtil.commonOnFailure(
                    wrapper.getBusinessTag(),
                    wrapper.getOriginalMsg(),
                    error,
                    wrapper.getRetryCount()
            );
            // 2. 触发业务Service的自定义失败回调
            wrapper.getCallback().onFailure(error);
            // 3. 通用重试逻辑（由NettyCallbackUtil控制）
            if (wrapper.getRetryCount() < NettyClientManager.MAX_RECONNECT_COUNT) {
                wrapper.setRetryCount(wrapper.getRetryCount() + 1);
                // 重新注册回调（用于重试）
                callbackMap.put(messageId, wrapper);
                // 调用NettyClientManager重试发送
                nettyClientManager.asyncSendMessage(wrapper.getFinalMsg(), 5, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            log.error("[NettyCallbackCenter]触发失败回调异常，消息ID[{}]", messageId, e);
        }
    }

    /**
     * 超时回调任务：用Netty的EventLoop线程池执行（避免额外线程）
     */
    private void scheduleTimeoutCallback(String messageId) {
        nettyClientManager.getGroup().schedule(() -> {
            if (callbackMap.containsKey(messageId)) {
                triggerFailure(messageId, new RuntimeException("消息超时未响应（" + TIMEOUT_SECONDS + "秒）"));
            }
            log.info("[NettyCallbackCenter]超时任务触发，消息ID[{}]", messageId);
        }, TIMEOUT_SECONDS, TimeUnit.SECONDS);
    }

    /**
     * 更新回调包装类中的finalMsg
     * @param messageId 消息ID
     * @param finalMsg 包含messageId的最终消息
     */
    public void updateFinalMsg(String messageId, String finalMsg) {
        CallbackWrapper wrapper = callbackMap.get(messageId);
        if (wrapper != null) {
            wrapper.setFinalMsg(finalMsg);
        } else {
            log.warn("[NettyCallbackCenter]消息ID[{}]不存在，无法更新finalMsg", messageId);
        }
    }
}